package com.diven.common.tree.model;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

import com.diven.common.tree.utils.TreeUtil;

import weka.core.Attribute;
import weka.core.Instances;
import weka.core.Utils;

public class ClassifierTree implements Serializable {

	public static final long serialVersionUID = -1635481717888437935L;
	
	/** 
	 * <p> 当前节点ID</p>
	 */
	private Integer treeNo;
	
	/** 
	 * <p> 当前节点UUID</p>
	 */
	private String UUID;
	
	/** 
	 * <p> 标题信息（用于打印树）， 数据可选 </p>
	 */
	private Instances instances = null;
	/** 
	 * <p> 当前树节点的子节点数.</p>
	 */
	private ClassifierTree[] childNodes;
	/** 
	 * <p> 当前节点使用的哪个特征进行分裂（特征的索引号）</p>
	 */
	private int splitAttribute = -1;
	/** 
	 * <p> 当前特征的分箱点</p>
	 */
	private BinPoints splitPoints;
	/** 
	 * <p> 每个分支的训练实例的比例 </p>
	 */
	private double[] instancesProps = null;
	/**
	 * <p> 若标签为Nominal类型，数组代表各个标签枚举值的占比 </p>
	 * <p> 若标签为Numeric类型，数据代表均值。</p>
	 */
	private double[] classProbs = null;
	/**
	 * <p> 若标签为Nominal类型，数组代表各个标签枚举值出现的次数</p>
	 * <p> 若标签为Numeric类型，数据代表平方误差和权重之和。</p>
	 */
	private double[] distributions = null;
	
	/**
	 * <p> 用于缓存特征推荐列表 </p>
	 */
	private List<FeatureSpeculate> speculates;
	
	public ClassifierTree() {}

	public Integer getTreeNo() {
		return treeNo;
	}

	public void setTreeNo(Integer treeNo) {
		this.treeNo = treeNo;
	}

	public String getUUID() {
		return UUID;
	}

	public void setUUID(String uUID) {
		UUID = uUID;
	}

	public Instances getInstances() {
		return instances;
	}

	public void setInstances(Instances instances) {
		this.instances = instances;
	}

	public ClassifierTree[] getChildNodes() {
		return childNodes;
	}

	public void setChildNodes(ClassifierTree[] childNodes) {
		this.childNodes = childNodes;
	}

	public int getSplitAttribute() {
		return splitAttribute;
	}

	public void setSplitAttribute(int splitAttribute) {
		this.splitAttribute = splitAttribute;
	}

	public BinPoints getSplitPoints() {
		return splitPoints;
	}

	public void setSplitPoints(BinPoints splitPoints) {
		this.splitPoints = splitPoints;
	}

	public double[] getInstancesProps() {
		return instancesProps;
	}

	public void setInstancesProps(double[] instancesProps) {
		this.instancesProps = instancesProps;
	}

	public double[] getClassProbs() {
		return classProbs;
	}

	public void setClassProbs(double[] classProbs) {
		this.classProbs = classProbs;
	}

	public double[] getDistributions() {
		return distributions;
	}

	public void setDistributions(double[] distributions) {
		this.distributions = distributions;
	}

	
	public List<FeatureSpeculate> getSpeculates() {
		return speculates;
	}

	public void setSpeculates(List<FeatureSpeculate> speculates) {
		this.speculates = speculates;
	}
	
	/**
	 * 将模型转换为图
	 * @return
	 * @throws Exception
	 */
	public String toGraph(int sampleCount) throws Exception {
		StringBuffer graph = new StringBuffer();
		if(this.splitAttribute < 0) {
			//叶子节点
			if(this.distributions != null && Utils.sum(this.distributions) > 0) {
				graph
				.append("N").append(Integer.toHexString(this.UUID.hashCode())).append(" ")
				.append("[label=\"").append("#").append(this.treeNo).append("\\n")
				.append(TreeUtil.getDotTableByTree(this, sampleCount))
				.append("\"").append("shape=box]\n");
			}
			else {
				graph
				.append("N").append(Integer.toHexString(this.UUID.hashCode())).append(" ")
				.append("[fillcolor=\"#ffff00\"").append(" label=\"").append("#").append(this.treeNo).append("\\n")
				.append(TreeUtil.getDotTableByTree(this, sampleCount))
				.append("\"").append("shape=box]\n");
			}
		}
		else {
			//非叶子节点
			Attribute attribute = this.instances.attribute(this.splitAttribute);
			String attributeName = Utils.backQuoteChars(attribute.name());
        	double width = 1.0 * attributeName.length() / 8;
        	if(width > 3.5) {
        		graph
				.append("N").append(Integer.toHexString(this.UUID.hashCode())).append(" ")
				.append("[width=").append(width).append(" label=\"").append("#").append(this.treeNo).append("\\n")
				.append(TreeUtil.getDotTableByTree(this, sampleCount))
				.append(Utils.backQuoteChars(attribute.name()))
				.append("\"").append("shape=box]\n");
        	}
        	else {
        		graph
				.append("N").append(Integer.toHexString(this.UUID.hashCode())).append(" ")
				.append("[label=\"").append("#").append(this.treeNo).append("\\n")
				.append(TreeUtil.getDotTableByTree(this, sampleCount))
				.append(Utils.backQuoteChars(attribute.name()))
				.append("\"").append("shape=box]\n");
        	}
        	// 下面是构建关系的
			for(int i=0; i< this.childNodes.length; i++) {
				graph
				.append("N").append(Integer.toHexString(this.UUID.hashCode()))
				.append("->")
				.append("N").append(Integer.toHexString(childNodes[i].UUID.hashCode())).append(" ")
				.append("[label=\"")
				.append(TreeUtil.getBinsToLabel(this.splitPoints).get(i))
				.append("\"]").append("\n");
				//递归处理
				graph.append(childNodes[i].toGraph(sampleCount));
			}
		}
		return graph.toString();
	}
	

	@Override
	public String toString() {
		return "ClassifierTree [" +"\n"
				+ "\t treeNo=" + treeNo+", \n"
				+ "\t UUID=" + UUID+", \n"
				+ "\t childNodes=" + Arrays.toString(childNodes) +", \n"
				+ "\t splitAttribute=" + splitAttribute  +", \n"
				+ "\t splitPoints=" + splitPoints  +", \n"
				+ "\t instancesProps="+ Arrays.toString(instancesProps)  +", \n"
				+ "\t classProbs=" + Arrays.toString(classProbs)  +", \n"
				+ "\t distributions="+ Arrays.toString(distributions)  +", \n"
				+ "]\n";
	}

}
